Understanding Local, Global, and Nonlocal Scope in Python
Course: Python Training
Topic: Understanding Local, Global, and Nonlocal Scope
Course Link: Python Training Course at JustAcademy
Demo Link: Register for a Course Demo
Website: Visit JustAcademy
1. Introduction to Scope in Python
Scope in Python refers to the region of a program where a variable, function, or name can be accessed. Understanding scope helps programmers control how data is stored, accessed, and modified throughout an application.
Python uses the LEGB rule to determine where a name is searched for when it is referenced. LEGB stands for Local, Enclosing, Global, and Built-in scope.
- Local scope: Names defined inside the current function.
- Enclosing scope: Names in an outer function surrounding a nested function.
- Global scope: Names defined at the module level.
- Built-in scope: Names provided by Python, such as
print(), len(), and sum().
Local, global, and nonlocal scope are especially important when working with functions, nested functions, callbacks, and reusable code.
2. Learning Objectives
- Understand what variable scope means in Python.
- Identify local and global variables.
- Access global variables from inside functions.
- Modify global variables using the
global keyword.
- Understand enclosing scope in nested functions.
- Modify enclosing variables using the
nonlocal keyword.
- Apply the LEGB name-resolution rule.
- Avoid common scope-related errors.
- Use scope effectively in practical programming tasks.
3. What Is a Variable?
A variable is a name that refers to an object in Python. The object may be a number, string, list, dictionary, function, or another data type.
name = "Aarav"
age = 22
course = "Python"
print(name)
print(age)
print(course)
Output:
Aarav
22
Python
Where a variable is defined influences where its name can be accessed. This is known as its scope.
4. Understanding Local Scope
4.1 What Is Local Scope?
A local variable is a name assigned inside a function. It is generally available only within that function's local scope.
def greet():
message = "Hello, Python!"
print(message)
greet()
Output:
Hello, Python!
In this example, message is a local variable because it is assigned inside greet().
4.2 Accessing a Local Variable Outside a Function
A local variable normally cannot be accessed directly outside the function in which it is defined.
def show_number():
number = 100
print(number)
show_number()
# This raises NameError because number is local:
# print(number)
Output:
100
The function can access number, but the surrounding code cannot access that local name after the function call. Returning the value is one way to make the result available outside the function.
def get_number():
number = 100
return number
result = get_number()
print(result)
Output:
100
4.3 Local Variables in Different Functions
Two functions can use the same local variable name without sharing the variable binding. Each function has its own local scope.
def function_one():
value = 10
print("Function one:", value)
def function_two():
value = 50
print("Function two:", value)
function_one()
function_two()
Output:
Function one: 10
Function two: 50
The two variables named value are local to different functions, so assigning one does not change the other.
4.4 Parameters Are Local Names
Function parameters are local names within the function unless other scope rules apply to a different name.
def calculate_square(number):
result = number * number
return result
print(calculate_square(6))
Output:
36
Both number and result are local names in calculate_square().
5. Understanding Global Scope
5.1 What Is Global Scope?
A global variable is a name defined at the module level, outside functions and classes. It can be accessed from functions in the same module, provided it is not shadowed by a local binding.
course_name = "Python Programming"
def display_course():
print(course_name)
display_course()
print(course_name)
Output:
Python Programming
Python Programming
The name course_name is defined at the module level. The function reads the global value because it does not create a local variable with the same name.
5.2 Reading a Global Variable Inside a Function
A function can read a global variable without using the global keyword, as long as the function does not assign to that name locally.
tax_rate = 0.18
def calculate_tax(amount):
return amount * tax_rate
print(calculate_tax(1000))
Output:
180.0
The function reads the global variable tax_rate and uses it to calculate the tax.
5.3 Local Variables Can Shadow Global Variables
If a function assigns to a name, Python generally treats that name as local throughout the function unless the name is declared with global or nonlocal. A local name can therefore shadow a global name.
message = "Global message"
def display_message():
message = "Local message"
print(message)
display_message()
print(message)
Output:
Local message
Global message
The assignment inside display_message() creates a local binding. It does not modify the global variable.
6. Using the global Keyword
6.1 What Is the global Keyword?
The global keyword tells Python that a name used in a function refers to the module-level binding rather than a new local binding. It is useful when a function must reassign a global variable.
counter = 0
def increment():
global counter
counter += 1
increment()
increment()
increment()
print(counter)
Output:
3
Without global counter, the augmented assignment would treat counter as a local name and raise an error because its local value would be read before being assigned.
6.2 Modifying a Global Variable
balance = 5000
def deposit(amount):
global balance
balance += amount
deposit(1500)
print(balance)
Output:
6500
The function modifies the module-level balance binding. For larger applications, passing the balance into a function and returning the updated value is often easier to test and maintain.
6.3 The Difference Between Reading and Reassigning
You do not need the global keyword merely to read a global variable. You need it when assigning to that global name from within a function.
total = 100
def show_total():
print(total) # Reading the global name
def update_total():
global total
total = 200
show_total()
update_total()
show_total()
Output:
100
200
6.4 Modifying a Mutable Global Object
The global keyword is not required merely to mutate an existing global list or dictionary, because the name is not being rebound.
students = ["Aarav", "Meera"]
def add_student(name):
students.append(name)
add_student("Kabir")
print(students)
Output:
['Aarav', 'Meera', 'Kabir']
However, assigning a new list to students inside the function would require a global students declaration if the intention were to rebind the module-level name.
7. Understanding Enclosing Scope
7.1 What Is Enclosing Scope?
Enclosing scope exists when one function is defined inside another function. The inner function can access names from its enclosing function, provided those names are available in the enclosing scope.
def outer_function():
message = "Hello from the outer function"
def inner_function():
print(message)
inner_function()
outer_function()
Output:
Hello from the outer function
The name message is local to outer_function(), but it is in the enclosing scope of inner_function().
7.2 Accessing an Enclosing Variable
def outer():
language = "Python"
def inner():
print("Learning:", language)
inner()
outer()
Output:
Learning: Python
The inner function reads the variable defined by the outer function. No nonlocal declaration is necessary because the inner function does not reassign the name.
8. Using the nonlocal Keyword
8.1 What Is the nonlocal Keyword?
The nonlocal keyword allows a nested function to reassign a name in the nearest enclosing function scope where that name already exists. It does not refer to the module-level global scope.
def outer():
count = 0
def inner():
nonlocal count
count += 1
inner()
inner()
print(count)
outer()
Output:
2
The nonlocal count declaration makes the assignment update the enclosing function's count binding.
8.2 Modifying an Enclosing Variable
def create_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = create_counter()
print(counter())
print(counter())
print(counter())
Output:
1
2
3
The inner function remembers the enclosing variable between calls. This pattern is known as a closure and can be used to maintain private state.
8.3 What Happens Without nonlocal?
If a nested function assigns to a name, Python normally treats that name as local to the nested function. Reading it before the local assignment can produce an UnboundLocalError.
def outer():
value = 10
def inner():
# Python treats value as local because of the assignment.
# This causes UnboundLocalError:
# print(value)
# value = 20
pass
inner()
outer()
To reassign the enclosing function's variable, declare it using nonlocal value inside the nested function.
8.4 nonlocal Does Not Refer to a Global Variable
The nonlocal keyword requires a binding in an enclosing function scope. It cannot be used to modify a module-level variable directly.
message = "Global"
def outer():
message = "Enclosing"
def inner():
nonlocal message
message = "Updated enclosing"
inner()
print(message)
outer()
print(message)
Output:
Updated enclosing
Global
The inner function changes the enclosing variable. The module-level message remains unchanged.
9. Understanding the LEGB Rule
Python follows the LEGB rule when resolving names. It searches the following scopes in order:
- Local: The current function's local scope.
- Enclosing: The local scopes of enclosing functions, from the nearest outward.
- Global: The current module's global scope.
- Built-in: Names provided by Python's built-in namespace.
name = "Global"
def outer():
name = "Enclosing"
def inner():
name = "Local"
print(name)
inner()
outer()
print(name)
Output:
Local
Global
The inner function prints its local value because local scope is searched before enclosing and global scope. The final statement prints the module-level value.
9.1 Example of Enclosing and Global Resolution
language = "Java"
def outer():
language = "Python"
def inner():
print(language)
inner()
outer()
Output:
Python
The inner function does not define language locally, so Python searches the enclosing scope and finds "Python" before reaching the global value "Java".
9.2 Built-in Scope Example
def display_items(items):
return len(items)
print(display_items(["Pen", "Book", "Bag"]))
Output:
3
The name len is found in Python's built-in namespace because no closer binding for that name is defined in the function or surrounding scopes.
10. Comparison of Local, Global, and Nonlocal Scope
| Feature |
Local |
Global |
Nonlocal |
| Where it applies |
Current function |
Current module |
Nearest enclosing function scope |
| Typical declaration |
No special keyword |
global when rebinding a global name inside a function |
nonlocal when rebinding an enclosing name |
| Can be read in a nested function? |
Only if it is in that function's lookup chain |
Yes, if not shadowed |
Yes, when in an enclosing function scope |
| Can be reassigned? |
Yes, locally |
Yes, with global inside a function |
Yes, with nonlocal inside a nested function |
| Common use |
Temporary calculations |
Module-wide configuration or shared state |
State retained by nested functions |
11. Practical Example: Global Configuration
Global variables are sometimes useful for module-level configuration values that functions need to read.
APP_NAME = "Student Portal"
MAX_LOGIN_ATTEMPTS = 3
def display_configuration():
print("Application:", APP_NAME)
print("Maximum attempts:", MAX_LOGIN_ATTEMPTS)
display_configuration()
Output:
Application: Student Portal
Maximum attempts: 3
Configuration values that should remain unchanged by ordinary program operations are often written in uppercase by convention. Python does not enforce constants merely because their names are uppercase.
12. Practical Example: Local Variables for Calculations
Local variables are suitable for intermediate values that are only needed while a function performs a calculation.
def calculate_bill(price, quantity, discount_percent):
subtotal = price * quantity
discount = subtotal * discount_percent / 100
final_amount = subtotal - discount
return final_amount
bill = calculate_bill(500, 4, 10)
print("Final bill:", bill)
Output:
Final bill: 1800.0
The variables subtotal, discount, and final_amount are local. Keeping them inside the function avoids unnecessary changes to module-level state.
13. Practical Example: nonlocal for a Score Tracker
A nested function can use nonlocal to maintain a score across several calls.
def create_score_tracker():
score = 0
def update_score(points):
nonlocal score
score += points
return score
return update_score
add_points = create_score_tracker()
print(add_points(10))
print(add_points(15))
print(add_points(5))
Output:
10
25
30
The variable score belongs to the enclosing function. The nested function updates it and retains the state between calls.
14. Practical Example: Shopping Cart with a Closure
A closure can keep a cart's state private while providing functions to update and inspect it.
def create_cart():
items = []
def add_item(item):
items.append(item)
def get_items():
return items.copy()
def get_total():
return sum(item["price"] for item in items)
return add_item, get_items, get_total
add_item, get_items, get_total = create_cart()
add_item({"name": "Book", "price": 250})
add_item({"name": "Pen", "price": 30})
print(get_items())
print("Total:", get_total())
Output:
[{'name': 'Book', 'price': 250}, {'name': 'Pen', 'price': 30}]
Total: 280
The nested functions access the enclosing items list. In this example, list mutation is sufficient, so no nonlocal declaration is required. Returning a copy from get_items() prevents callers from directly modifying the internal list through that returned list.
15. Common Scope-Related Errors
| Error or problem |
Cause |
Solution |
NameError |
A name is not available in the current lookup chain. |
Define the name in an appropriate scope or pass it as a parameter. |
UnboundLocalError |
A local variable is read before it has been assigned. |
Initialize the local variable first or use the correct scope declaration. |
| Unexpected global value |
A local variable shadows a global variable. |
Use clearer variable names and check where each name is assigned. |
| Global variable not updated |
A function creates a local binding instead of rebinding the global one. |
Use global when global reassignment is truly needed. |
| Enclosing variable not updated |
A nested function creates a local binding instead of modifying the enclosing one. |
Use nonlocal when updating an enclosing function variable. |
| Invalid nonlocal declaration |
No suitable binding exists in an enclosing function scope. |
Ensure the variable is defined in an enclosing function. |
| Unexpected shared state |
Several functions depend on mutable global data. |
Prefer parameters, return values, or a well-defined state-management design. |
16. Best Practices for Managing Scope
- Prefer local variables for temporary calculations and function-specific data.
- Pass values into functions as parameters instead of relying on hidden global dependencies.
- Return updated values when doing so makes a function easier to test and reuse.
- Use global variables sparingly, especially for data that changes frequently.
- Use
global only when a function genuinely needs to rebind a module-level name.
- Use
nonlocal for appropriate nested-function state, such as a closure counter.
- Choose descriptive names to reduce accidental shadowing.
- Keep functions small and focused on a single responsibility.
- Avoid using names such as
list, str, or sum for your own variables because they shadow built-ins.
- Test functions independently, including their boundary cases.
17. Practice Exercises
Exercise 1: Local Scope
Create a function called display_age() with a local variable named age. Print the age inside the function and observe what happens if you try to access it outside.
Exercise 2: Global Scope
Create a global variable named company_name. Write a function that displays it without modifying it.
Exercise 3: Using global
Create a global counter initialized to zero. Write a function that increments it each time the function is called.
Exercise 4: Enclosing Scope
Create an outer function with a variable called language. Define an inner function that reads and prints that variable.
Exercise 5: Using nonlocal
Create a nested function that maintains a running total. Use nonlocal to update the total on each call.
Exercise 6: LEGB Rule
Define a variable with the same name in global, enclosing, and local scopes. Print the name from the inner function and explain which value Python selects.
Exercise 7: Refactor Global State
Rewrite a function that updates a global balance so that it accepts the current balance and deposit amount as parameters and returns the updated balance instead.
18. Mini Project: Account Balance Manager
Objective: Create a simple account manager that demonstrates local variables, global configuration, parameters, and return values.
CURRENCY = "INR"
def deposit(balance, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
return balance + amount
def withdraw(balance, amount):
if amount <= 0:
raise ValueError("Withdrawal must be positive")
if amount > balance:
raise ValueError("Insufficient balance")
return balance - amount
def display_balance(balance):
print(f"Current balance: {CURRENCY} {balance:.2f}")
balance = 5000
balance = deposit(balance, 1500)
display_balance(balance)
balance = withdraw(balance, 2000)
display_balance(balance)
Output:
Current balance: INR 6500.00
Current balance: INR 4500.00
Project explanation:
CURRENCY is a module-level configuration value read by the display function.
balance and amount are parameters used by the deposit and withdrawal functions.
- The arithmetic variables inside these functions remain local.
- The updated balance is returned and assigned by the calling code.
- The program avoids modifying a global balance from inside the deposit and withdrawal functions.
19. Interview Questions and Answers
Q1. What is scope in Python?
Scope is the region in which a name can be accessed. Python resolves names using the LEGB rule.
Q2. What is a local variable?
A local variable is a name defined within a function and normally accessible within that function's local scope.
Q3. What is a global variable?
A global variable is a name defined at the module level. It can be read inside functions unless shadowed by a closer binding.
Q4. What does the global keyword do?
The global keyword declares that assignments to a name inside a function should rebind the module-level name.
Q5. What does the nonlocal keyword do?
The nonlocal keyword declares that assignments to a name inside a nested function should rebind a name in the nearest suitable enclosing function scope.
Q6. Can a function read a global variable without using global?
Yes. The keyword is unnecessary when the function only reads the global name and does not reassign it.
Q7. What is the LEGB rule?
LEGB stands for Local, Enclosing, Global, and Built-in. It describes the order Python uses to look up names.
Q8. What is variable shadowing?
Variable shadowing occurs when a name in a nearer scope hides a name with the same spelling in an outer scope.
Q9. What is a closure?
A closure is a function that retains access to variables from its enclosing scope even after the outer function has returned.
Q10. Can nonlocal modify a global variable?
No. nonlocal refers to a binding in an enclosing function scope. Use global for rebinding a module-level name inside a function.
Q11. What is UnboundLocalError?
It is an error raised when a local variable is accessed before it has been assigned a value in the current function execution.
Q12. Why should global variables be used sparingly?
Changing global state can create hidden dependencies, complicate testing, and make programs harder to maintain.
Q13. Does modifying a global list require global?
Mutating an existing list, such as calling append(), does not require global. Rebinding the global name to a different list does.
Q14. What happens when a local variable has the same name as a global variable?
The local binding takes precedence inside that function, while the global binding remains unchanged unless explicitly rebound.
Q15. What is the difference between global and nonlocal?
global targets a module-level binding, while nonlocal targets a binding in an enclosing function scope.
20. Quick Revision Checklist
- Scope determines where names can be accessed.
- Local variables belong to the current function scope.
- Global variables are defined at the module level.
- Functions can read global variables without declaring them global.
- The
global keyword is used to rebind a module-level name inside a function.
- Enclosing scope is created by nested functions.
- The
nonlocal keyword allows a nested function to rebind an enclosing function's variable.
- The LEGB rule describes Python's name lookup order.
- Local names can shadow enclosing and global names.
- Closures retain access to enclosing variables.
- Parameters and return values often reduce the need for mutable global state.
- Practice scope rules using small functions and nested-function examples.
21. Conclusion
Understanding local, global, and nonlocal scope is essential for writing clear and reliable Python programs. Local scope keeps temporary variables inside functions, global scope makes module-level names available across a module, and nonlocal scope enables nested functions to update variables belonging to enclosing functions.
The LEGB rule explains how Python finds names. By using local variables by default, limiting global state, and applying global and nonlocal only when appropriate, you can create programs that are easier to understand, debug, test, and maintain.
Continue learning: Explore the Python Training Course, register for a course demo, or visit the JustAcademy website for more learning resources.